package com.tvserials.notification;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.*;
import android.widget.*;
import com.espian.showcaseview.ShowcaseView;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.tvserials.notification.adapters.NavDrawerItem;
import com.tvserials.notification.adapters.NavigationAdapter;
import com.tvserials.notification.adapters.TvEpisodeGridAdapter;
import com.tvserials.notification.adapters.TvEpisodeListAdapter;
import com.tvserials.notification.dto.Episode;
import com.tvserials.notification.dto.EpisodeMedia;
import com.tvserials.notification.dto.Serial;
import com.tvserials.notification.exceptions.NetworkConnectionException;
import com.tvserials.notification.exceptions.PageParseException;
import com.tvserials.notification.parser.LostFilmHtmlPageParser;
import com.tvserials.notification.preferences.SharedPreferencesUtils;
import com.tvserials.notification.preferences.ShowCaseManager;
import com.tvserials.notification.services.NotificationService;
import com.tvserials.notification.services.ServiceFactory;
import com.tvserials.notification.utils.DisplayToastMessage;
import com.tvserials.notification.utils.NetworkUtils;
import com.tvserials.notification.utils.VOUtils;
import com.tvserials.notification.vo.SourceType;
import com.tvserials.notification.vo.TvEpisodeFilterVO;

import java.util.*;

public class MainAppActivity extends Activity {

    private static final String TAG = "MainAppActivity";
    public static final String NAV_POSITIION_EXTRA = "NAV_EXTRA";
    public static final String AUTHORITY = "com.tvserials.notification.sync";
    private static final String AD_UNIT_ID = "ca-app-pub-2466562031542232/2688657705";
    private TvEpisodeListAdapter listAdapter;
    private TvEpisodeGridAdapter gridAdapter;

    private ListView listView;
    private GridView gridView;

    private ShowcaseView showcaseView;
    private boolean isTileLayout = false;
    private MenuItem menuListType;
    private List<NavDrawerItem> navDrawerItems;
    private NavigationAdapter navigationAdapter;
    private ListView mDrawerList;
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    private String[] navigationTitles;
    private NotificationService integrationService;
    //identify currently selected navigation item
    private SourceType sourceType;
    private AccountManager accountManager;
    private String token;
    private AdView adView;

    @Override
    @TargetApi(Build.VERSION_CODES.CUPCAKE)
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        retrieveAccountData();
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main);

        int uploadInterval = SharedPreferencesUtils.getSyncIntervalTime(getApplicationContext());
        updateSyncTimer(uploadInterval);

        // Добавляем данные для ListView
        listView = (ListView) findViewById(R.id.listView1);
        listView.setOnItemClickListener(new EpisodeItemClickListener());

        gridView = (GridView) findViewById(R.id.gridView);
        gridView.setOnItemClickListener(new EpisodeItemClickListener());

        initializeNavigationPanel();

        isTileLayout = SharedPreferencesUtils.isTileLayout(getApplicationContext());
        loadEpisodesFromCache();
        //application opened, need to cleanup all notification from bar
        cancelAllNotifications();

        if (NetworkUtils.isConnected(getApplicationContext())) {
            initializeAdMob();
        }
    }

    private void initializeAdMob() {
        adView = new AdView(this);
        adView.setAdSize(AdSize.SMART_BANNER);
        adView.setAdUnitId(AD_UNIT_ID);
        // Add the AdView to the view hierarchy. The view will have no size
        // until the ad is loaded.
        RelativeLayout layout = (RelativeLayout) findViewById(R.id.adViewPanel);
        layout.addView(adView);

        // Create an ad request. Check logcat output for the hashed device ID to
        // get test ads on a physical device.
        AdRequest adRequest = new AdRequest.Builder()
                .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
//                .addTestDevice("INSERT_YOUR_HASHED_DEVICE_ID_HERE")
                .build();

        // Start loading the ad in the background.
        adView.loadAd(adRequest);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (adView != null) {
            adView.resume();
        }
    }

    @Override
    public void onPause() {
        if (adView != null) {
            adView.pause();
        }
        super.onPause();
    }

    /**
     * Called before the activity is destroyed.
     */
    @Override
    public void onDestroy() {
        // Destroy the AdView.
        if (adView != null) {
            adView.destroy();
        }
        super.onDestroy();
    }

    private void retrieveAccountData() {
        accountManager = AccountManager.get(this);
        String userName = getUserName();
        if (userName == null) {
            Intent intent = AccountManager.newChooseAccountIntent(null, null,
                    new String[]{LoginActivity.ACCOUNT_TYPE}, false, null, null, null, null);
            startActivityForResult(intent, 4);
        } else {
            requestToken(new Account(userName, LoginActivity.ACCOUNT_TYPE));
        }
    }

    private String getUserName() {
        return SharedPreferencesUtils.getUser(getApplicationContext());
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case 1:
                Log.w(TAG, "Backed from Settings activity" + requestCode);
                int uploadInterval = SharedPreferencesUtils.getSyncIntervalTime(getApplicationContext());
                updateSyncTimer(uploadInterval);
                return;
            case 2:
                Log.w(TAG, "Backed from sign activity");
                filterEpisodesCache();
                fillListViewData(getEpisodesFromCache());
                return;
            case 3:
                Log.w(TAG, "Activity");
                return;
            case 4:
                Log.w(TAG, "Selected account type");
                if (resultCode == RESULT_OK) {
                    String userAccountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
                    requestToken(new Account(userAccountName, LoginActivity.ACCOUNT_TYPE));
                }
                return;
            default:
                super.onActivityResult(requestCode, resultCode, data);
        }
    }

    private void requestToken(Account userAccount) {
        accountManager.getAuthToken(userAccount, LoginActivity.AUTHTOKEN_TYPE, null, this,
                new OnTokenAcquired(), null);
    }

    private class OnTokenAcquired implements AccountManagerCallback<Bundle> {

        @Override
        public void run(AccountManagerFuture<Bundle> result) {
            try {
                Bundle bundle = result.getResult();
                Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
                if (launch != null) {
                    startActivityForResult(launch, 5);
                } else {
                    token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
                    String accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
                    SharedPreferencesUtils.setUser(getApplicationContext(), accountName);
                    //TODO put here code which will load data
                }
            } catch (Exception e) {
                //TODO failed for some reason to get token
            }
        }
    }

    /**
     * Clear all notifications from notification bar
     */
    private void cancelAllNotifications() {
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancelAll();
    }

    /**
     * Initialize navigation panel
     */
    private void initializeNavigationPanel() {
        List<SourceType> sourceTypes = SharedPreferencesUtils.getSelectedSourceTypes(getApplicationContext());

        navigationTitles = getResources().getStringArray(R.array.navigationTitles);

        NavDrawerItem lostFilmItem = new NavDrawerItem(navigationTitles[1], R.drawable.lostfilm_icon, SourceType.LOST_FILM);
        NavDrawerItem newStudioItem = new NavDrawerItem(navigationTitles[2], R.drawable.newstudio_icon, SourceType.NEW_STUDIO);
        NavDrawerItem seasonVarItem = new NavDrawerItem(navigationTitles[3], R.drawable.season_var_icon, SourceType.SEASON_VAR);
        List<NavDrawerItem> allItems = Arrays.asList(lostFilmItem, newStudioItem, seasonVarItem);

        navDrawerItems = new ArrayList<NavDrawerItem>();
        if (sourceTypes.size() > 1) {
            navDrawerItems.add(new NavDrawerItem(navigationTitles[0], R.drawable.app_icon, SourceType.ALL));
        }
        for (SourceType type : sourceTypes) {
            NavDrawerItem navItemByType = getNavItemByType(allItems, type);
            if (navItemByType != null) {
                navDrawerItems.add(navItemByType);
            }
        }

        mDrawerList = (ListView) findViewById(R.id.list_slidermenu);

        navigationAdapter = new NavigationAdapter(getApplicationContext(), navDrawerItems);
        mDrawerList.setAdapter(navigationAdapter);
        mDrawerList.setOnItemClickListener(new NavigationItemClickHandler());

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

        ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeButtonEnabled(true);
        }

        mDrawerToggle = new NavigationActionBarDrawerToggle();
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        int position = SharedPreferencesUtils.getNavigationItemPosition(getApplicationContext());
        Log.w(TAG, "Get saved nav position " + position);
        position = getIntent().getIntExtra(NAV_POSITIION_EXTRA, position);
        Log.w(TAG, "Requested intent position " + position);
        sourceType = SourceType.getSourceTypeByPosition(position);
        updateActionBarLogo(sourceType);
    }

    private NavDrawerItem getNavItemByType(List<NavDrawerItem> allItems, SourceType type) {
        for (NavDrawerItem item : allItems) {
            if (item.getSourceType().equals(type)) {
                return item;
            }
        }
        return null;
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggls
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu items for use in the action bar
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        if (SharedPreferencesUtils.getSerialsList(getApplicationContext()).isEmpty()) {
            showcaseView = ShowCaseManager.showCaseSerials(this);
        }
        menuListType = menu.findItem(R.id.menuListType);

        if (menuListType != null) {
            menuListType.setIcon((isTileLayout) ? R.drawable.ic_as_list : R.drawable.ic_as_grid);
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (showcaseView != null) {
            showcaseView.hide();
        }
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle presses on the action bar items
        switch (item.getItemId()) {
            case R.id.menuRefresh:
                Log.w(TAG, "refresh pressed");
                refreshEpisodesList();
                //
                Runnable runnable = new Runnable() {

                    @Override
                    public void run() {
                        refreshLostFilmMedia();
                    }
                };
                if (sourceType.equals(SourceType.LOST_FILM)) {
                    new Thread(runnable).start();
                }
                return true;
            case R.id.menuSign:
                Log.w(TAG, "sign pressed");
                Intent intent = new Intent(this, SignSerialsActivity.class);
                startActivityForResult(intent, 2);
                return true;
            case R.id.menuSettings:
                Log.w(TAG, "ic_settings pressed");
                Intent i = new Intent(this, UserSettingActivity.class);
                startActivityForResult(i, 1);
                return true;
            case R.id.menuListType:
                isTileLayout = !isTileLayout;
                SharedPreferencesUtils.saveTileLayout(getApplicationContext(), isTileLayout);
                loadEpisodesFromCache();
                return true;
            case R.id.menuLogout:
                SharedPreferencesUtils.setUser(getApplicationContext(), null);
                retrieveAccountData();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * Temporary fix to refresh list of media for lostfilm serials
     */
    private void refreshLostFilmMedia() {
        List<Episode> episodesFromCache = getEpisodesFromCache();
        List<Episode> episodes = VOUtils.filterBySourceTypes(episodesFromCache,
                Arrays.asList(SourceType.LOST_FILM));
        LostFilmHtmlPageParser htmlPageParser = new LostFilmHtmlPageParser();
        try {
            for (Episode episode : episodes) {
                List<EpisodeMedia> medias = htmlPageParser.readMediaData(episode);
                episode.setMediaQualities(medias);
            }
        } catch (NetworkConnectionException e) {
            e.printStackTrace();
        }
        episodes.addAll(VOUtils.filterBySourceTypes(episodesFromCache,
                Arrays.asList(SourceType.NEW_STUDIO)));
        episodes = VOUtils.sortByDate(episodes);
        updateEpisodesCache(episodes);
    }

    private void refreshEpisodesList() {
        if (NetworkUtils.isConnected(this)) {
            new LoadLatestEpisodes().execute();
        } else {
            runOnUiThread(new DisplayToastMessage(this, R.string.no_network));
            loadEpisodesFromCache();
        }
    }

    private void loadEpisodesFromCache() {
        List<Episode> episodesFromCache = getEpisodesFromCache();
        if (episodesFromCache != null) {
            fillListViewData(episodesFromCache);
        }
    }

    private List<Episode> getEpisodesFromCache() {
        return SharedPreferencesUtils.getEpisodes(getApplicationContext());
    }

    /**
     * Filter episodes in cache based on signed serials
     */
    private void filterEpisodesCache() {
        List<Episode> episodesFromCache = getEpisodesFromCache();
        List<Serial> serials = SharedPreferencesUtils.getSerialsList(getApplicationContext());
        episodesFromCache = VOUtils.filterBySerials(episodesFromCache, serials);
        updateEpisodesCache(episodesFromCache);
    }

    private void updateEpisodesCache(List<Episode> episodesFromCache) {
        SharedPreferencesUtils.saveEpisodes(getApplicationContext(), episodesFromCache);
    }

    private void updateSyncTimer(int uploadInterval) {
        String userName = getUserName();
        if (userName != null) {
            Account account = new Account(userName, LoginActivity.ACCOUNT_TYPE);
            if (uploadInterval != 0) {
                long triggerAtMillis = uploadInterval * 60;
                ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), triggerAtMillis);
            }
            Log.w(TAG, "Updated sync interval to min: " + uploadInterval);
            ContentResolver.setSyncAutomatically(account, AUTHORITY, uploadInterval != 0);
        }
    }

    private void fillListViewData(List<Episode> episodes) {
        episodes = VOUtils.filterBySourceTypes(episodes, getSourceTypes());
        if (isTileLayout) {
            gridAdapter = new TvEpisodeGridAdapter(getApplicationContext(), R.layout.chapter_items_grid_layout, episodes);
            gridView.setAdapter(gridAdapter);
            gridView.setVisibility(View.VISIBLE);
            listView.setVisibility(View.GONE);
            if (menuListType != null) {
                menuListType.setTitle(R.string.list);
                menuListType.setIcon(R.drawable.ic_as_list);
            }
        } else {
            listAdapter = new TvEpisodeListAdapter(getApplicationContext(), R.layout.chapter_items_list_layout, episodes);
            listView.setAdapter(listAdapter);
            listView.setVisibility(View.VISIBLE);
            gridView.setVisibility(View.GONE);
            if (menuListType != null) {
                menuListType.setTitle(R.string.tile);
                menuListType.setIcon(R.drawable.ic_as_grid);
            }
        }
    }

    public List<SourceType> getSourceTypes() {
        if (SourceType.ALL.equals(sourceType)) {
            return SharedPreferencesUtils.getSelectedSourceTypes(getApplicationContext());
        }
        return Arrays.asList(sourceType);
    }

    private class LoadLatestEpisodes extends AsyncTask<Void, Void, List<Episode>> {

        @Override
        protected void onPreExecute() {
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected List<Episode> doInBackground(Void... params) {
            List<Episode> cachedEpisodes = null;
            try {
                List<Serial> serialList = SharedPreferencesUtils.getSerialsList(getApplicationContext());
                cachedEpisodes = getEpisodesFromCache();
                TvEpisodeFilterVO filterVO = getTvEpisodeFilterVO(cachedEpisodes, serialList);
                integrationService = ServiceFactory.getNotificationService();
                List<Episode> episodes = integrationService.getEpisodes(filterVO);
                if (VOUtils.isListNotEmpty(cachedEpisodes)) {
                    episodes.addAll(cachedEpisodes);
                }
                List<Episode> episodeList = VOUtils.strictList(episodes, SharedPreferencesUtils.getHistoryNo(getApplicationContext()));
                episodeList = new ArrayList<Episode>(new HashSet<Episode>(episodeList));
                return VOUtils.sortByDate(episodeList);
            } catch (NetworkConnectionException e) {
                runOnUiThread(new DisplayToastMessage(getApplicationContext(), R.string.network_timeout));
            } catch (PageParseException e) {
                runOnUiThread(new DisplayToastMessage(getApplicationContext(), R.string.parse_error));
            }
            return cachedEpisodes;
        }

        private TvEpisodeFilterVO getTvEpisodeFilterVO(List<Episode> cachedEpisodes, List<Serial> serialList) {
            TvEpisodeFilterVO filterVO = new TvEpisodeFilterVO(1, null, serialList);
            filterVO.setSourceTypes(getSourceTypes());
            if (VOUtils.isListNotEmpty(cachedEpisodes)) {
                Date latestDate = VOUtils.getLatestDate(cachedEpisodes, getSourceTypes());
                filterVO.setLastSyncDate(latestDate);
            }
            return filterVO;
        }

        @Override
        protected void onPostExecute(List<Episode> episodes) {
            setProgressBarIndeterminateVisibility(false);

            Date syncTime = new Date();
            if (episodes != null && !episodes.isEmpty()) {
                fillListViewData(episodes);

                syncTime = episodes.get(0).getReleaseDate();
                updateEpisodesCache(episodes);
            }
            SharedPreferencesUtils.updateSyncDate(getApplicationContext(), syncTime);
        }
    }

    private class EpisodeItemClickListener implements AdapterView.OnItemClickListener {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Log.w(TAG, "click episode");
            ArrayAdapter chapterAdapter = (ArrayAdapter) parent.getAdapter();

            final Episode itemByPosition = (Episode) chapterAdapter.getItem(position);

            PopupMenu popup = new PopupMenu(view.getContext(), view);
            if (VOUtils.isListNotEmpty(itemByPosition.getMediaQualities())) {
                for (EpisodeMedia rssVO : itemByPosition.getMediaQualities()) {
                    popup.getMenu().add(rssVO.getSize());
                }
                popup.setOnMenuItemClickListener(new OnMenuItemClickListener(itemByPosition));
                popup.show();
            }
        }

        private class OnMenuItemClickListener implements PopupMenu.OnMenuItemClickListener {

            private final Episode itemByPosition;

            public OnMenuItemClickListener(Episode itemByPosition) {
                this.itemByPosition = itemByPosition;
            }

            @Override
            public boolean onMenuItemClick(MenuItem item) {
                Log.w(TAG, "menu" + item);
                String title = item.getTitle().toString();
                EpisodeMedia rssByType = itemByPosition.getMediaBySize(title);
                openBrowser(rssByType.getLink());
                return false;
            }
        }
    }

    private class NavigationItemClickHandler implements AdapterView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            sourceType = navDrawerItems.get(position).getSourceType();

            mDrawerList.setItemChecked(position, true);
            mDrawerList.setSelection(position);

            updateActionBarLogo(sourceType);

            mDrawerLayout.closeDrawer(mDrawerList);
            fillListViewData(getEpisodesFromCache());
            //save selected position
            SharedPreferencesUtils.saveNavigationItemPosition(getApplicationContext(), sourceType.ordinal());
        }
    }

    /**
     * Update action bar logo and title based on provided source type
     *
     * @param sourceType -
     */
    private void updateActionBarLogo(SourceType sourceType) {
        NavDrawerItem navDrawerItem = getNavItemByType(navDrawerItems, sourceType);
        if (navDrawerItem != null) {
            setTitle(navDrawerItem.getTitle());
            ActionBar actionBar = getActionBar();
            if (actionBar != null) {
                actionBar.setIcon(navDrawerItem.getIcon());
            }
        }
    }

    private void openBrowser(String url) {
        Intent browserIntent = new Intent(Intent.ACTION_VIEW);
        browserIntent.setData(Uri.parse(url));
        startActivity(browserIntent);
    }

    private class NavigationActionBarDrawerToggle extends ActionBarDrawerToggle {
        public NavigationActionBarDrawerToggle() {
            super(MainAppActivity.this, MainAppActivity.this.mDrawerLayout, R.drawable.ic_navigation_drawer, R.string.app_name, R.string.app_name);
        }

        public void onDrawerClosed(View view) {
            ActionBar actionBar = getActionBar();
            if (actionBar != null) {
                actionBar.setTitle(getTitle());
            }
            // calling onPrepareOptionsMenu() to show action bar icons
            invalidateOptionsMenu();
        }

        public void onDrawerOpened(View drawerView) {
            ActionBar actionBar = getActionBar();
            if (actionBar != null) {
                actionBar.setTitle(getTitle());
            }
            // calling onPrepareOptionsMenu() to hide action bar icons
            invalidateOptionsMenu();
        }
    }
}
